/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package 解析.node.node;

import 解析.node.others.TreeInfo;
import 解析.Matcher;
import static 解析.Pattern.GREEDY;
import static 解析.Pattern.LAZY;

/**
 *
 * @author yanxi
 */
public class Curly extends Node { 
    public Node atom;
    public int type;
    public int cmin;
    public int cmax;

    public Curly(Node node, int cmin, int cmax, int type) {
        this.atom = node;
        this.type = type;
        this.cmin = cmin;
        this.cmax = cmax;
        getChildren().add(node); 
    }
    
    @Override
    public boolean match(Matcher matcher, int i, CharSequence seq) {
        int j;
        for (j = 0; j < cmin; j++) {
            if (atom.match(matcher, i, seq)) {
                i = matcher.last;
                continue;
            }
            return false;
        }
        if (type == GREEDY) {
            return match0(matcher, i, j, seq);
        } else if (type == LAZY) {
            return match1(matcher, i, j, seq);
        } else {
            return match2(matcher, i, j, seq);
        }
    }

    // Greedy match.
    // i is the index to start matching at
    // j is the number of atoms that have matched
    boolean match0(Matcher matcher, int i, int j, CharSequence seq) {
        if (j >= cmax) {
            // We have matched the maximum... continue with the rest of
            // the regular expression
            return next.match(matcher, i, seq);
        }
        int backLimit = j;
        while (atom.match(matcher, i, seq)) {
            // k is the length of this match
            int k = matcher.last - i;
            if (k == 0) // Zero length match
            {
                break;
            }
            // Move up index and number matched
            i = matcher.last;
            j++;
            // We are greedy so match as many as we can
            while (j < cmax) {
                if (!atom.match(matcher, i, seq)) {
                    break;
                }
                if (i + k != matcher.last) {
                    if (match0(matcher, matcher.last, j + 1, seq)) {
                        return true;
                    }
                    break;
                }
                i += k;
                j++;
            }
            // Handle backing off if match fails
            while (j >= backLimit) {
                if (next.match(matcher, i, seq)) {
                    return true;
                }
                i -= k;
                j--;
            }
            return false;
        }
        return next.match(matcher, i, seq);
    }

    // Reluctant match. At this point, the minimum has been satisfied.
    // i is the index to start matching at
    // j is the number of atoms that have matched
    boolean match1(Matcher matcher, int i, int j, CharSequence seq) {
        for (;;) {
            // Try finishing match without consuming any more
            if (next.match(matcher, i, seq)) {
                return true;
            }
            // At the maximum, no match found
            if (j >= cmax) {
                return false;
            }
            // Okay, must try one more atom
            if (!atom.match(matcher, i, seq)) {
                return false;
            }
            // If we haven't moved forward then must break out
            if (i == matcher.last) {
                return false;
            }
            // Move up index and number matched
            i = matcher.last;
            j++;
        }
    }

    boolean match2(Matcher matcher, int i, int j, CharSequence seq) {
        for (; j < cmax; j++) {
            if (!atom.match(matcher, i, seq)) {
                break;
            }
            if (i == matcher.last) {
                break;
            }
            i = matcher.last;
        }
        return next.match(matcher, i, seq);
    }

    @Override
    public boolean study(TreeInfo info) {
        // Save original info
        int minL = info.minLength;
        int maxL = info.maxLength;
        boolean maxV = info.maxValid;
        boolean detm = info.deterministic;
        info.reset();

        atom.study(info);

        int temp = info.minLength * cmin + minL;
        if (temp < minL) {
            temp = 0xFFFFFFF; // arbitrary large number
        }
        info.minLength = temp;

        if (maxV & info.maxValid) {
            temp = info.maxLength * cmax + maxL;
            info.maxLength = temp;
            if (temp < maxL) {
                info.maxValid = false;
            }
        } else {
            info.maxValid = false;
        }

        if (info.deterministic && cmin == cmax) {
            info.deterministic = detm;
        } else {
            info.deterministic = false;
        }
        return next.study(info);
    } 
    
    @Override
    public String toString(){
        //return "["+this.getClass().getName()+"]"+"type:"+type+",cmin:"+cmin+",cmax:"+cmax;
        String s=atom.toString();
        
        if(cmin==0&&cmax==1){
            s+="?";
        }
        else if(cmax==2147483647){
            if(cmin==0)s+="*";
            else if(cmin==1)s+="+";
            else s+="{"+cmin+",}";
        }
        else{
            if(cmin==cmax)s+="{"+cmin+"}";
            else s+="{"+cmin+","+cmax+"}";
        }
        
        if(type==1)s+="?";
        else if(type==2)s+="+";
        return s;
    }
}
